package com.siyoumi.component;

import com.siyoumi.exception.EnumSys;
import com.siyoumi.exception.XException;
import com.siyoumi.util.*;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
public class XRedisLock {
    /**
     * 无超时时间
     *
     * @param key
     * @param func
     * @return
     */
    public static XReturn lockFunc(String key, Function<String, XReturn> func) {
        return lockFunc(key, func, -1);
    }

    /**
     * redis锁
     *
     * @param key
     * @param func
     * @param timeoutSeconds 超时时间（秒） -1：无超时时间
     */
    public static XReturn lockFunc(String key, Function<String, XReturn> func, Integer timeoutSeconds) {
        RLock lock = XRedis.getBean().getLock(key);

        XReturn r = XReturn.getR(EnumSys.REDIS_ERR.getErrcode());
        lock.lock(timeoutSeconds, TimeUnit.SECONDS);
        try {
            r = func.apply(key);
        } catch (Exception ex) {
            r.setErrMsg(ex.getMessage());
        } finally {
            lock.unlock();
        }

        return r;
    }


    public static XReturn tryLockFunc(String key, Function<String, XReturn> func) {
        return tryLockFunc(key, func, 60);
    }

    /**
     * 获取不到锁，立即返回不等待
     *
     * @param key
     * @param func
     * @param timeoutSeconds
     */
    public static XReturn tryLockFunc(String key, Function<String, XReturn> func, Integer timeoutSeconds) {
        if (timeoutSeconds <= 0) {
            throw new XException("timeoutSeconds error: " + timeoutSeconds);
        }

        XRedis redis = XRedis.getBean();

        Integer maxSeconds = 10 * 60; //最多锁10分钟
        timeoutSeconds = Math.min(timeoutSeconds, maxSeconds);

        String token = XApp.getStrID();
        Boolean getLock = redis.setIfExists(key, token, timeoutSeconds);
        if (!getLock) {
            return EnumSys.REDIS_GETLOCK_ERR.getR();
        }

        XReturn r = XReturn.getR(EnumSys.REDIS_ERR.getErrcode());
        try {
            r = func.apply(key);
        } catch (Exception ex) {
            ex.printStackTrace();
            r.setErrMsg(ex.getMessage());
        } finally {
            redis.delIfEqualsVal(key, token);
        }

        return r;
    }
}
